#ifndef _BASE
#define _BASE

// TODO: GENERAL - add unit tests for all functions
// TODO: GENERAL - assess thread-safety of STRING_GEN, framebuffer and all functions that use static vars regularly, etc.
// TODO: GENERAL - find a better system to manage multiple custom color channels

// INCLUDES --------------------------------------------------------------------------
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "math.h"

// TYPEDEFS --------------------------------------------------------------------------

// bool, true/false
typedef unsigned char      bool;
#define true               (bool)1
#define false              (bool)0

// floating point
typedef float              fp32;
typedef double             fp64;
typedef fp32               fp;

// byte
typedef char               i8;
typedef unsigned char      ui8;

// single
typedef short              i16;
typedef unsigned short     ui16;

// int
typedef int                i32;
typedef unsigned int       ui32;

// long
typedef long               i64;
typedef unsigned long      ui64;

// long long
typedef long long          i128;
typedef unsigned long long ui128;

// DEFINES ---------------------------------------------------------------------------
#define DEBUG

#ifdef DEBUG
  // ------------------------------------------------------------------
  /* msg -> message string to print
     ... -> alternative string variables
  */
  #define PRINT(msg, ...) \
  { \
    printf((msg), __VA_ARGS__); \
  };

  // sanity check(s) for various errors

  /* f   -> function name as string
     p   -> pointer to check
     ... -> alternative return value
  */
  #define GUARD_NULL(f, p, ...) \
  { \
    if ((p) == NULL) \
    { \
      PRINT( "NULL pointer as target! [%s->%s:%d]\n", (f), __FILE__, __LINE__ ); \
      return __VA_ARGS__; \
    } \
  }

  /* f   -> function name as string
     k   -> value to check
     ... -> alternative return value
  */
  #define GUARD_ZERO(f, k, ...) \
  { \
    if ((k) == 0) \
    { \
      PRINT( "division by zero! [%s->%s:%d]\n", (f), __FILE__, __LINE__ ); \
      return __VA_ARGS__; \
    } \
  }
  // ------------------------------------------------------------------
#else // DEBUG
  // ------------------------------------------------------------------
  // no performance penalty here...

  #define PRINT(msg, ...)       {};
  #define GUARD_NULL(f, p, ...) { return __VA_ARGS__; };
  #define GUARD_ZERO(f, k, ...) { return __VA_ARGS__; };
  // ------------------------------------------------------------------
#endif // DEBUG

// CLAMP -----------------------------------------------------------------------------

/* Clamp to: [a,b]
   v   -> value to clamp
   min -> min value for v
   max -> max value for v
*/
#define CLAMP(v, min, max) \
{ \
  if      ((v) < (min)) (v) = (min); \
  else if ((v) > (max)) (v) = (max); \
}

/* Clamp to: [a,b[
   v   -> value to clamp
   min -> min value for v
*/
#define CLAMP_LOW(v, min) { if ((v) < (min)) v = (min); }

/* Clamp to: ]a,b]H
   v   -> value to clamp
   max -> max value for v
*/
#define CLAMP_HIGH(v, max) { if ((v) > (max)) v = (max); }

// STRING GENERATOR ------------------------------------------------------------------
#define     MAX_STRING_GEN_SIZE 20000       // max size of string generator buffer
static i8   stringGen[MAX_STRING_GEN_SIZE]; // global string generator buffer
static i32  stringGenSuccess = -1;          // global snprintf status after STRING_GEN

/* String Generator
   f   -> format string
   ... -> alternative string variables

   clear memory,
   get return value of snprintf(),
   check for return value being (>= 0) and smaller than buffer size,
   
   if yes: assembling of string to the buffer succeeded,
           stringGenSuccess contains the amount of chars assembled
   if no:  buffer will contain an empty string after 2nd memory clear,
           stringGenSuccess contains -1  
*/

// TODO: check for possible overhead memset() before snprintf()
#define STRING_GEN(f, ...) \
{ \
  memset(stringGen, 0, sizeof(i8)*MAX_STRING_GEN_SIZE); \
  stringGenSuccess = snprintf(stringGen, MAX_STRING_GEN_SIZE, (f), __VA_ARGS__); \
  \
  if (stringGenSuccess < 0 || stringGenSuccess >= MAX_STRING_GEN_SIZE) \
  { \
    memset(stringGen, 0, MAX_STRING_GEN_SIZE); \
    stringGenSuccess = -1; \
  } \
};

/* fill copy function for lining up structs in a memory area.
   
   dest     -> destination memory (NEEDS TO BE OF ADEQUATE BYTE SIZE!!)
   src      -> src pointer to the struct
   byteSize -> byte size of the source struct
   amount   -> amount of copies that need to be lined up

   output will add up the src to look like this in dest memory:

   dest:     |           |
   src:      |A|
   byteSize: *someByteSize*
   amount:   5

   RESULT:   |AAAAA      |
*/
void FillCopy(void *dest, void *src, ui32 byteSize, ui32 amount);

#endif // _BASE
